import json
import requests
import urllib.parse as urlparse
import time
import hmac
import hashlib
import sys

from coin.exchange.base.kr_rest.auth_base import AuthKey
from coin2.api_client.common import (SyncQueryer)
from enum import Enum

class Side(Enum):
  BUY = "BUY"
  SELL = "SELL"

class OrderType(Enum):
  LIMIT = "LIMIT"
  MARKET = "MARKET"

class TimeInForce(Enum):
  GTC = "GTC"
  IOC = "IOC"
  FOK = "FOK"

class OrderState(Enum):
  ACTIVE = "ACTIVE"
  COMPLETED = "COMPLETED"
  CANCELED = "CANCELED"
  EXPIRED = "EXPIRED"
  REJECTED = "REJECTED"

class BitflyerAuth(requests.auth.AuthBase):
  def __init__(self, key):
    self._key = AuthKey.from_file(key)

  def _gen_signature(self, timestamp: str, method: str, path_with_query: str, body: str):
    assert timestamp, "timestamp must be not None and not empty!"
    assert method, "method must be not None and not empty!"
    assert path_with_query, "path_with_query must be not None and not empty!"

    before_signature = ''.join(filter(None, [timestamp, method, path_with_query, body])).encode()

    mac = hmac.new(self._key.secret_key_bytes, before_signature, digestmod = hashlib.sha256)

    return mac.hexdigest()

  def __call__(self, req: requests.PreparedRequest):
    parsed = urlparse.urlparse(req.url)
    path_with_query = parsed.path
    if parsed.query:
      path_with_query = path_with_query + "?" + parsed.query
    timestamp = str(int(time.time()))
    body = req.body
    if body != None and isinstance(body, bytes):
      body = body.decode()
    signature = self._gen_signature(timestamp, req.method, path_with_query, body)

    req.headers.update({'ACCESS-KEY': self._key.access_key})
    req.headers.update({'ACCESS-TIMESTAMP': timestamp})
    req.headers.update({'ACCESS-SIGN': signature})
    req.prepare_headers(req.headers)

    # print(req.headers)
    # print(req.body)
    # print(req.url)

    return req

class BitflyerPrivateClient():
  def __init__(self, key_path: str, ip=None):
    self._auth = BitflyerAuth(key_path)
    self._url = "https://api.bitflyer.com"

    self._query = SyncQueryer(api_host=self._url, auth=self._auth, ip=ip)

  def get_order(
    self,
    product_code: str,
    child_order_state: OrderState = None,
    child_order_id: str = None,
    child_order_acceptance_id: str = None,
    parent_order_id: str = None,
  ):
    assert product_code, "product_code must be not None and not empty!"

    method = "GET"
    params = {
      'product_code': product_code,
      'child_order_state': child_order_state and child_order_state.value,
      'child_order_id': child_order_id,
      'child_order_acceptance_id': child_order_acceptance_id,
      'parent_order_id': parent_order_id,
    }
    path = "/v1/me/getchildorders"

    response = self._query.query(method=method, path=path, params=params)
    return response.json()

  def cancel_order(
    self,
    product_code: str,
    child_order_id: str = None,
    child_order_acceptance_id: str = None,
  ):
    assert product_code, "product_code must be not None and not empty!"
    only_child_order_id = child_order_id != None and child_order_acceptance_id == None
    only_child_order_acceptance_id = child_order_id == None and child_order_acceptance_id != None
    one_and_only_one = only_child_order_id or only_child_order_acceptance_id
    assert one_and_only_one, "child_order_id and child_order_acceptance_id have one and only one!"

    method = "POST"
    json = {
      'product_code': product_code,
      'child_order_id': child_order_id,
      'child_order_acceptance_id': child_order_acceptance_id,
    }
    path = "/v1/me/cancelchildorder"

    response = self._query.query(method=method, path=path, json=json)
    return response.json()

  def submit_order(
    self,
    product_code: str,
    child_order_type: OrderType,
    side: Side,
    size: str,
    price: str = None,
    minute_to_expire: str = None,
    time_in_force: TimeInForce = None,
  ):
    assert product_code, "product_code must be not None and not empty!"
    assert child_order_type, "child_order_type must be not None!"
    assert side, "side must be not None!"
    assert size, "size must be not None and not empty!"
    
    method = "POST"
    json = {
      'product_code': product_code,
      'child_order_type': child_order_type.value,
      'side': side.value,
      'size': size,
      'price': price,
      'minute_to_expire': minute_to_expire,
      'time_in_force': time_in_force and time_in_force.value,
    }
    path = "/v1/me/sendchildorder"

    response = self._query.query(method=method, path=path, json=json)
    return response.json()

# api doc url
# https://lightning.bitflyer.com/docs?lang=en

if __name__ == "__main__":
  key_path = sys.argv[1]
  
  bitflyer = BitflyerPrivateClient(key_path)
  info = bitflyer.get_order("BTC_JPY", OrderState.ACTIVE)
  # info = bitflyer.cancel_order('BTC_JPY', '11234124254')
  # info = bitflyer.submit_order("BTC_JPY", OrderType.LIMIT, Side.SELL, "0.0000001", "1000000000", "60", TimeInForce.GTC)
  print(json.dumps(info, indent=2))
